﻿using HtmlParser.Dom;
using System.Text;
using System.Collections.Generic;
using System;
using System.Diagnostics;

namespace HtmlParser.Html
{
    public class HtmlDocument 
    {
        private char[] chars = null;
        private int index = 0;
        private char[] cache = new char[0x10000];
        private int charIndex = 0;
        private StringBuilder builder = new StringBuilder(0x100);
        private int maxLength = 0;
        public  XmlDocument Parse(string html)
        {
            return Parse(html.ToCharArray());
        }
        public  XmlDocument Parse(char[] htmls)
        {
            var doc = new XmlDocument();
            IList<XmlNode> nodeList = new List<XmlNode>();
            WordParse(nodeList, htmls, doc);
            return doc;
        }

        private void WordParse(IList<XmlNode> nodeList, char[] htmls, XmlDocument doc)
        {

            if (htmls == null || htmls.Length <= 0)
            {
                return;
            }
            index = 0;
            chars = htmls;
            maxLength = htmls.Length;//文本总长度
            char c = GetChar();

            string tempName = string.Empty;//临时的名称
            string tempName2 = string.Empty;
            XmlNode tempEle = null;// 当前临时的元素
            XmlAttribute tempAtt = null;
            XmlText tempText = null;
            int lastTag = -1;// 上一次标记的类型
            int cutTag = -1;//
            //结构化文档分为标记，属性，内容(内容中要注意转义字符)，注释

            while (index + 1 <= maxLength)
            {
                switch (c)
                {
                    case '<':
                        {
                            #region 元素处理
                            bool isEnd = false;
                            c = GetChar();
                            c = ProcessWhitespace(c);
                            if (c == '!' || c == '?')
                            {
                                #region 如果是指令或者注释
                                break;
                                #endregion
                            }
                            else if (c == '/')
                            {
                                c = GetChar();
                                isEnd = true;
                            }
                            // Not ' ' '>' '/'
                            while (!IsElementNameEnd(c))
                            {
                                SetChar(c);
                                c = GetChar();
                            }
                            tempName = GetString();
                            tempEle = doc.CreateElement(tempName);
                            // 如果当前是空格 说明还有属性标签
                            if (HtmlHelper.IsWhiteSpace(c))
                            {
                                tempEle.Tag = Laxer.StartTag;
                                nodeList.Add(tempEle);
                                //如果当前为空格就不向后多取一个字符
                                lastTag = Laxer.StartTag;
                            }
                            else if (HtmlHelper.IsMoreThan(c))
                            {
                                // 如果当前是 > 说明元素头已经关闭
                                nodeList.Add(tempEle);
                                if (isEnd)
                                {
                                    tempEle.Tag = Laxer.EndTag;
                                    lastTag = Laxer.EndTag;
                                }
                                else
                                {
                                    tempEle.Tag = Laxer.ClosedTag;
                                    lastTag = Laxer.ClosedTag;
                                }
                            }
                            else if (HtmlHelper.IsBackSlash(c))
                            {
                                // 如果当前是 / 说明元素关闭
                                nodeList.Add(tempEle);
                                tempEle.Tag = Laxer.ClosedTag;
                                //再向后取一个 >
                                c = GetChar();
                                lastTag = Laxer.ClosedTag;
                            }
                            else
                            {

                            }


                            #endregion
                            break;
                        }
                    case ' ':
                        {
                            #region 属性处理
                            c = ProcessWhitespace(c);
                            if (lastTag == Laxer.StartTag || lastTag == Laxer.DOCUMENT_TYPE_NODE || lastTag == Laxer.ATTRIBUTE_NODE || lastTag == Laxer.AttEmpty)
                            {
                                #region Att
                                while (!IsAttributeEnd(c))
                                {
                                    SetChar(c);
                                    c = GetChar();
                                }
                                if (HtmlHelper.IsMoreThan(c) || HtmlHelper.IsBackSlash(c))
                                {
                                    //Att end.当前元素的Attribute都遍历完了.
                                    if (HtmlHelper.IsBackSlash(c))
                                    {
                                        c = GetChar();
                                    }
                                    break;
                                }

                                tempName = GetString();
                                c = ProcessWhitespace(c);

                                tempName2 = string.Empty;
                                lastTag = Laxer.AttEmpty;
                                if (HtmlHelper.IsEqual(c))
                                {
                                    c = GetChar();
                                    c = ProcessWhitespace(c);
                                    if (IsMark(c))
                                    {
                                        c = GetChar();
                                        while (!IsMark(c))
                                        {
                                            SetChar(c);
                                            c = GetChar();
                                        }
                                        tempName2 = GetString();
                                        lastTag = Laxer.ATTRIBUTE_NODE;
                                    }
                                    else
                                    {
                                        while (HtmlHelper.IsWhiteSpace(c))
                                        {
                                            SetChar(c);
                                            c = GetChar();
                                        }
                                        tempName2 = GetString();
                                        lastTag = Laxer.ATTRIBUTE_NODE;
                                    }
                                }

                                tempAtt = doc.CreateAttribute(tempName);
                                tempAtt.Value = tempName2;
                                if (lastTag == Laxer.ATTRIBUTE_NODE)
                                {
                                    tempAtt.Tag = Laxer.ATTRIBUTE_NODE;
                                }
                                else
                                {
                                    tempAtt.Tag = Laxer.AttEmpty;
                                }
                                tempEle.Attributes.AddNode(tempAtt);

                                #endregion
                                if (lastTag == Laxer.ATTRIBUTE_NODE)
                                {
                                    c = GetChar();
                                }
                                else
                                {
                                    c = SetBackChar();
                                }
                            }
                            else
                            {
                                if (HtmlHelper.IsMoreThan(c))
                                {

                                }
                                else
                                {
                                    c = GetChar();
                                }
                            }

                            #endregion
                            break;
                        }
                    case '>':
                        {
                            #region 内容处理
                            if (lastTag == Laxer.StartTag || lastTag == Laxer.ATTRIBUTE_NODE || lastTag == Laxer.AttEmpty || lastTag == Laxer.ClosedTag || lastTag == Laxer.Comment || lastTag == Laxer.EndTag || lastTag == Laxer.DOCUMENT_TYPE_NODE)
                            {
                                c = GetChar();
                                if (c == char.MinValue)
                                {
                                    break;
                                }
                                // 以后这里需要加入 
                                while (!HtmlHelper.IsLessThan(c))
                                {
                                    SetChar(c);
                                    c = GetChar();
                                }

                                tempName = GetString();
                                tempText = doc.CreateTextNode(tempName);
                                tempText.Tag = Laxer.Content;
                                nodeList.Add(tempText);
                            }
                            #endregion
                            break;
                        }
                    case '!':
                        {
                            #region 指令处理
                            c = GetChar();
                            if (c != '-')
                            {
                                while (!IsDocType(c))
                                {
                                    SetChar(c);
                                    c = GetChar();
                                }
                                tempName = GetString();
                                tempEle = doc.CreateElement(tempName);
                                tempEle.Tag = lastTag = Laxer.DOCUMENT_TYPE_NODE;
                                nodeList.Add(tempEle);
                            }
                            else
                            {
                                #region 注释处理
                                c = GetChar();
                                if (c == '-')
                                {
                                    c = GetChar();
                                    while (!IsComment(c))
                                    {
                                        SetChar(c);
                                        c = GetChar();
                                    }


                                    c = GetChar();
                                    c = GetChar();
                                    tempName = GetString();
                                    tempEle = doc.CreateComment(tempName);
                                    tempEle.Tag = lastTag = Laxer.Comment;
                                    nodeList.Add(tempEle);
                                }
                                #endregion
                            }
                            #endregion
                        }
                        break;
                    default:
                        {
                            c = GetChar();
                            break;
                        }
                }
            }

            return;
        }

        #region 字符判断类
        /// <summary>
        /// 判断当前字符是否是一个元素结束 <aaa></aaa>
        /// </summary>
        /// <param name="c">当前字符</param>
        /// <returns>如果当前字符是 ' ', '>' ,'/' 返回true,否则为false</returns>
        private bool IsElementNameEnd(char c)
        {
            return HtmlHelper.IsWhiteSpace(c) || HtmlHelper.IsMoreThan(c) || HtmlHelper.IsBackSlash(c);
        }
        /// <summary>
        /// 判断当前字符是否是一个属性结束 
        /// </summary>
        /// <param name="c">当前字符</param>
        /// <returns></returns>
        private bool IsAttributeEnd(char c)
        {
            return HtmlHelper.IsEqual(c) || HtmlHelper.IsWhiteSpace(c) || HtmlHelper.IsMoreThan(c) || HtmlHelper.IsBackSlash(c);
        }

        private bool IsMark(char c)
        {
            return HtmlHelper.IsSingleQuotation(c) || HtmlHelper.IsDoubleQuotation(c);
        }
        private bool IsComment(char c)
        {
            if (index + 3 < maxLength)
            {
                if (c == '-' && chars[index] == '-' && chars[index + 1] == '>')
                {
                    return true;
                }
            }
            return false;
        }

        private bool IsDocType(char c)
        {

            if ((c == '>') || (c == '/' && chars[index] == '>'))
            {
                return true;
            }

            return false;
        }
        #endregion

        #region 字符操作类
        /// <summary>
        /// 得到一个字符
        /// </summary>
        /// <returns>字符</returns>
        private char GetChar()
        {
            if (index + 1 > maxLength)
            {
                return char.MinValue;
            }
            else
            {
                var c = chars[index++];
                //Debug.Write(c);
                return c;
            }
        }

        private char SetBackChar()
        {
            --index;
            return chars[--index];
        }

        private string GetString()
        {
            builder.Clear();
            for (int i = 0; i < charIndex; i++)
            {
                builder.Append(cache[i]);
            }
            charIndex = 0;
            return builder.ToString().Trim();
        }

        private void SetChar(char c)
        {
            cache[charIndex++] = c;
        }

        private char ProcessWhitespace(char c)
        {
            while (HtmlHelper.IsWhiteSpace(c))
            {
                c = GetChar();
            }

            return c;
        }
        #endregion

    }// end for class

}
